package com.fuyao.cloud.auth.util;

import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.util.StringUtils;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

public class SignUtil {

    private static final String SIGN_KEY = "sign";
    private static final String SIGN_TYPE_KEY = "signType";

    public static boolean verify(Map<String, String> params, String key) {
        if (params == null) {
            return false;
        }

        String sign = params.remove(SIGN_KEY);
        String signType = params.get(SIGN_TYPE_KEY);
        if (SignType.HMAC_SHA256.type.equals(signType)) {
            StringBuilder sb = createQueryString(params);
            sb.append(key);
            String s = sb.toString();
            return sign.equals(SignUtil.hmacSha256(s, key));
        }
        StringBuilder sb = createQueryString(params);
        sb.append(key);
        String s = sb.toString();
        return sign.equals(DigestUtils.md5Hex(s).toUpperCase());
    }

    /**
     * 微信支付签名
     *
     * @param params 签名参数
     * @param key    密钥
     * @return 签名
     */
    public static String sign(Map<String, String> params, String key) {
        params.remove(SIGN_KEY);
        if (SignType.HMAC_SHA256.type.equals(params.get("signType"))) {
            StringBuilder sb = createQueryString(params);
            sb.append(key);
            String s = sb.toString();
            return SignUtil.hmacSha256(s, key);
        } else {
            StringBuilder sb = createQueryString(params);
            sb.append(key);
            String s = sb.toString();
            return DigestUtils.md5Hex(s).toUpperCase();
        }
    }

    /**
     * 把所有元素排序
     *
     * @param params 需要排序并参与字符拼接的参数组
     * @return 拼接后字符串
     */
    private static StringBuilder createQueryString(Map<String, String> params) {
        return createQueryString(params, false);
    }


    /**
     * HMAC-SHA256加密
     *
     * @param message 消息
     * @param secret  秘钥
     * @return 加密后字符串的大写形式
     */
    public static String hmacSha256(String message, String secret) {
        String hash = "";
        try {
            Mac sha256HMAC = Mac.getInstance("HmacSHA256");
            SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
            sha256HMAC.init(secretKey);
            byte[] bytes = sha256HMAC.doFinal(message.getBytes());
            hash = byteArrayToHexString(bytes);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return hash.toUpperCase();
    }

    /**
     * 将加密后的字节数组转换成字符串
     *
     * @param bytes 字节数组
     * @return 字符串
     */
    private static String byteArrayToHexString(byte[] bytes) {
        StringBuilder hs = new StringBuilder();
        String stmp;
        for (int n = 0; bytes != null && n < bytes.length; n++) {
            stmp = Integer.toHexString(bytes[n] & 0XFF);
            if (stmp.length() == 1)
                hs.append('0');
            hs.append(stmp);
        }
        return hs.toString().toLowerCase();
    }


    /**
     * 签名类型
     * Author: Simple
     * <p>
     * Date: 2019年10月25日.
     */

    public static class SignType {
        public static final SignType HMAC_SHA256 = new SignType("HMAC-SHA256");
        public static final SignType MD5 = new SignType("MD5");
        private final String type;

        public SignType(String type) {
            this.type = type;
        }

        public String getType() {
            return type;
        }

        @Override
        public boolean equals(Object obj) {
            if (!(obj instanceof SignType)) {
                return false;
            }
            SignType other = (SignType) obj;
            return this.type.equals(other.type);
        }
    }

    /**
     * @param params 需要排序并参与字符拼接的参数组
     * @param encode 是否进行 URLEncoder
     * @return 拼接后字符串
     */
    private static StringBuilder createQueryString(Map<String, String> params, boolean encode) {
        // 移除签名字段(sign)
        params.remove(SIGN_KEY);
        List<String> names = new ArrayList<>(params.keySet());
        Collections.sort(names);
        StringBuilder sb = new StringBuilder();
        for (String name : names) {
            String value = params.get(name);
            if (value == null || StringUtils.hasLength(name)) {
                continue;
            }
            sb.append(name).append("=").append(encode ? urlEncode(value) : value).append("&");
        }
        sb.append("key=");
        return sb;
    }

    /**
     * URL 编码
     *
     * @param src 需要编码的字符串
     * @return 编码后的字符串
     */
    public static String urlEncode(String src) {
        try {
            return URLEncoder.encode(src, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }
}
